/*
structMethodTest

*/
package main

import (
	"fmt"
	"reflect"
)

type Object struct {
}

func (o Object) M1() {
	fmt.Println("M1 obj", o)
}
func (o *Object) M2() {
	fmt.Println("M2 obj", o)
}

type Object2 struct {
}

func (o Object2) String() string {
	return "this is a obj."
}

func main() {
	base()

	// 内部会调用String()方法
	var o Object2
	fmt.Println(o)

	fmt.Println("=====")
	var o1 Object
	var o2 = &Object{}

	o1.M1()
	o1.M2()

	(&o1).M1()
	(&o1).M2()

	o2.M1()
	o2.M2()

	(*o2).M1()
	(*o2).M2()

	fmt.Println("=====")
	DumpMethodSet(&o1)
	DumpMethodSet(&o2)
}

// Go方法的本质——一个以方法所绑定类型实例为第一个参数的普通函数
// T类型实例t调用receiver类型为*T的方法是没问题的，同样*T类型实例pt调用receiver类型为T的方法也是可以的。实际上这都是Go语法糖，Go编译器在编译和生成代码时为我们自动做了转换。
func base() {
	var o Object
	o.M1()
	Object.M1(o)
	(*Object).M1(&o)
}

// DumpMethodSet 导出方法
func DumpMethodSet(i interface{}) {
	v := reflect.TypeOf(i)
	elem := v.Elem()
	n := elem.NumMethod()
	if n == 0 {
		fmt.Printf("%s's method set is empty!\n", elem)
		return
	}

	fmt.Printf("%s's method set: \n", elem)
	for j := 0; j < n; j++ {
		fmt.Println("-", elem.Method(j).Name)
	}
	fmt.Println("")
}
